/*
 * Complex.cs
 * 
 * Copyright (c) 2003-2005, dnAnalytics. All rights reserved.
*/


#region Using directives

using System;
using System.Runtime.InteropServices;
using System.Text;
using dnAnalytics.Resources;

#endregion Using directives

namespace dnAnalytics.Math
{
    ///<summary>complex double type.</summary>
    ///<remarks>See <see cref="ComplexMath">ComplexMath</see> for complex math functions.</remarks>
    ///<seealso cref="dnAnalytics.Math.ComplexMath"/>
    [Serializable]
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct Complex : IFormattable
    {
        #region Constants

        private static readonly Complex zero = new Complex(0);
        private static readonly Complex one = new Complex(1);
        private static readonly Complex nan = new Complex(Double.NaN, Double.NaN);
        private static readonly Complex infinity = new Complex(Double.PositiveInfinity, Double.PositiveInfinity);
        private static readonly Complex i = new Complex(0, 1);

        #endregion Constants

        #region Fields

        private readonly double real;
        private readonly double imag;

        #endregion Fields

        #region Constructors

        ///<summary>Constructor for complex double precision number type</summary>
        ///<param name="real">Real value of complex number expressed as <b>double</b>.</param>
        public Complex(double real) : this(real, 0) { }

        ///<summary>Constructor for complex double precision number type</summary>
        ///<param name="real">Real value of complex number expressed as <b>double</b>.</param>
        ///<param name="imaginary">Imaginary part of complex number expressed as <b>double</b>.</param>
        public Complex(double real, double imaginary)
        {
            this.real = real;
            this.imag = imaginary;
        }

        ///<summary>Created a <b>Complex</b> from the given string. The string can be in the
        ///following formats: <b>n</b>, <b>ni</b>, <b>n +/- ni</b>, <b>n,n</b>, <b>n,ni</b>,
        ///<b>(n,n)</b>, or <b>(n,ni)</b>, where n is a real number.</summary>
        ///<param name="value">The string to create the <b>Complex</b> from.</param>
        ///<exception cref="FormatException">if the n, is not a number.</exception>
        ///<exception cref="ArgumentNullException">if s, is <b>null</b>.</exception>
        public Complex(string value) : this(value, null) { }

        ///<summary>Created a <b>Complex</b> from the given string. The string can be in the
        ///following formats: <b>n</b>, <b>ni</b>, <b>n +/- ni</b>, <b>n,n</b>, <b>n,ni</b>,
        ///<b>(n,n)</b>, or <b>(n,ni)</b>, where n is a real number.</summary>
        ///<param name="value">The string to create the <b>Complex</b> from.</param>
        ///<param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        ///<exception cref="FormatException">if the n, is not a number.</exception>
        ///<exception cref="ArgumentNullException">if s, is <b>null</b>.</exception>
        public Complex(string value, IFormatProvider formatProvider)
        {
            this = Complex.Parse(value, formatProvider);
        }

        #endregion Constructors

        #region Properties

        ///<summary>Constant value of one.</summary>
        public static Complex One
        {
            get { return one; }
        }

        ///<summary>Constant value of zero.</summary>
        public static Complex Zero
        {
            get { return zero; }
        }

        ///<summary>Constant value of NaN.</summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1706:ShortAcronymsShouldBeUppercase", MessageId = "Member")]
        public static Complex NaN
        {
            get { return nan; }
        }

        ///<summary>Constant value of Infinity.</summary>
        public static Complex Infinity
        {
            get { return infinity; }
        }

        ///<summary>Constant value for I.</summary>
        public static Complex I
        {
            get { return i; }
        }

        ///<summary>Property to access Real component</summary>
        public double Real
        {
            get { return this.real; }
        }

        ///<summary>Property to access Imaginary component</summary>
        public double Imaginary
        {
            get { return this.imag; }
        }

        /// <summary>
        /// Returns the conjugate of the <b>Complex</b>.
        /// </summary>
        public Complex Conjugate
        {
            get { return ComplexMath.Conjugate(this); }
        }

        /// <summary>
        /// Returns the Absolute of the <b>Complex</b>.
        /// </summary>
        public double Absolute
        {
            get { return ComplexMath.Absolute(this); }
        }

        #endregion Properties

        #region Type Conversions

        ///<summary>Implicit conversion from double type</summary>
        public static implicit operator Complex(double value)
        {
            return new Complex(value);
        }

        ///<summary>Convert double type to Complex</summary>
        ///<param name="value"><b>double</b> variable as real to</param>
        public static Complex ToComplex(double value)
        {
            return new Complex(value);
        }

        #endregion

        #region Public Members

        ///<summary>Return the Hashcode for the <b>Complex</b></summary>
        ///<returns>The Hashcode representation of <b>Complex</b></returns>
        public override int GetHashCode()
        {
            return (int)System.Math.Exp(ComplexMath.Absolute(this));
        }

        ///<summary>Check if <b>Complex</b> variable is the same as another <b>Complex</b></summary>
        ///<param name="obj"><b>obj</b> to compare present <b>Complex</b> to.</param>
        ///<returns>Returns true if the two objects are the same object, or the the Real and Imaginary 
        ///components of both are equal, false otherwise</returns>
        public bool Equals(Complex obj)
        {
            return this.Real == obj.Real && this.Imaginary == obj.Imaginary;
        }

        ///<summary>Check if <b>Complex</b> variable is the same as another object</summary>
        ///<param name="obj"><b>obj</b> to compare present <b>Complex</b> to.</param>
        ///<returns>Returns true if the two objects are the same object, or the the Real and Imaginary 
        ///components of both are equal, false otherwise</returns>
        public override bool Equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (obj is Complex)
            {
                Complex rightSide = (Complex)obj;
                return this.Real == rightSide.Real && this.Imaginary == rightSide.Imaginary;
            }
            else
            {
                return false;
            }
        }

        ///<summary>Equal operator to compare two <b>Complex</b> variables</summary>
        ///<remarks>Returns false if the two variables are not equals using Equals function</remarks>
        public static bool operator ==(Complex o1, Complex o2)
        {
            return o1.Equals(o2);
        }

        ///<summary>Not Equal operator to compare two <b>Complex</b> variables</summary>
        ///<remarks>Returns false if the two variables are equal using Equals function</remarks>
        public static bool operator !=(Complex o1, Complex o2)
        {
            return !o1.Equals(o2);
        }

        ///<summary>Positive Operator</summary>
        public static Complex operator +(Complex value)
        {
            return value;
        }

        ///<summary>Positive Operator</summary>
        public static Complex Plus(Complex value)
        {
            return value;
        }

        ///<summary>Addition Operator</summary>
        public static Complex operator +(Complex leftSide, Complex rightSide)
        {
            return new Complex(leftSide.Real + rightSide.Real, leftSide.Imaginary + rightSide.Imaginary);
        }

        ///<summary>Addition Operator</summary>
        public static Complex Add(Complex leftSide, Complex rightSide)
        {
            return leftSide + rightSide;
        }

        ///<summary>Negate Operator</summary>
        public static Complex operator -(Complex value)
        {
            return new Complex(-value.Real, -value.Imaginary);
        }

        ///<summary>Negate Operator</summary>
        public static Complex Negate(Complex value)
        {
            return -value;
        }

        ///<summary>Subtraction Operator</summary>
        public static Complex operator -(Complex leftSide, Complex rightSide)
        {
            return new Complex(leftSide.Real - rightSide.Real, leftSide.Imaginary - rightSide.Imaginary);
        }

        ///<summary>Subtraction Operator</summary>
        public static Complex Subtract(Complex leftSide, Complex rightSide)
        {
            return leftSide - rightSide;
        }

        ///<summary>Multiplication Operator</summary>
        public static Complex operator *(Complex leftSide, Complex rightSide)
        {
            return new Complex(leftSide.Real * rightSide.Real - leftSide.Imaginary * rightSide.Imaginary, leftSide.Real * rightSide.Imaginary + leftSide.Imaginary * rightSide.Real);
        }

        ///<summary>Multiplication Operator</summary>
        public static Complex Multiply(Complex leftSide, Complex rightSide)
        {
            return leftSide * rightSide;
        }

        //based on Numerical Recipes's Cdiv
        ///<summary>Division Operator</summary>
        public static Complex operator /(Complex leftSide, Complex rightSide)
        {
            if (rightSide.real == 0.0 && rightSide.Imaginary == 0.0)
            {
                return Complex.NaN;
            }

            if (rightSide.IsInfinity() && !leftSide.IsInfinity())
            {
                return Complex.zero;
            }

            double real, imag;
            if (System.Math.Abs(rightSide.real) >= System.Math.Abs(rightSide.imag))
            {
                double temp = rightSide.imag / rightSide.real;
                double denom = rightSide.real + temp * rightSide.imag;
                real = (leftSide.real + temp * leftSide.imag) / denom;
                imag = (leftSide.imag - temp * leftSide.real) / denom;
            }
            else
            {
                double temp = rightSide.real / rightSide.imag;
                double denom = rightSide.imag + temp * rightSide.real;
                real = (leftSide.real * temp + leftSide.imag) / denom;
                imag = (leftSide.imag * temp - leftSide.real) / denom;
            }
            return new Complex(real, imag);
        }

        ///<summary>Division Operator</summary>
        public static Complex Divide(Complex leftSide, Complex rightSide)
        {
            return leftSide / rightSide;
        }

        ///<summary>Tests whether the the complex number is not a number.</summary>
        ///<returns>True if either the real or imaginary components are NaN, false otherwise.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1706:ShortAcronymsShouldBeUppercase", MessageId = "Member")]
        public bool IsNaN()
        {
            if (this == nan)
            {
                return true;
            }
            else
            {
                return (Double.IsNaN(real) || Double.IsNaN(imag));
            }
        }

        ///<summary>Tests whether the the complex number is infinite.</summary>
        ///<returns>True if either the real or imaginary components are infinite, false otherwise.</returns>
        public bool IsInfinity()
        {
            if (this == infinity)
            {
                return true;
            }
            else
            {
                return (Double.IsInfinity(real) || Double.IsInfinity(imag));
            }
        }

        // --- IFormattable Interface --
        ///<summary>A string representation of this <b>Complex</b>.</summary>
        ///<returns>The string representation of the value of <b>this</b> instance.</returns>
        public override string ToString()
        {
            return ToString(null, null);
        }

        ///<summary>A string representation of this <b>Complex</b>.</summary>
        ///<param name="format">A format specification.</param>
        ///<returns>The string representation of the value of <b>this</b> instance as specified by format.</returns>
        public string ToString(string format)
        {
            return ToString(format, null);
        }

        ///<summary>A string representation of this <b>Complex</b>.</summary>
        ///<param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        ///<returns>The string representation of the value of <b>this</b> instance as specified by provider.</returns>
        public string ToString(IFormatProvider formatProvider)
        {
            return ToString(null, formatProvider);
        }

        ///<summary>A string representation of this <b>Complex</b>.</summary>
        ///<param name="format">A format specification.</param>
        ///<param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        ///<returns>The string representation of the value of <b>this</b> instance as specified by format and provider.</returns>
        ///<exception cref="FormatException">if the n, is not a number.</exception>
        ///<exception cref="ArgumentNullException">if s, is <b>null</b>.</exception>		
        public String ToString(string format, IFormatProvider formatProvider)
        {
            if (IsNaN())
            {
                return "NaN";
            }
            if (IsInfinity())
            {
                return "IsInfinity";
            }

            StringBuilder ret = new StringBuilder();

            ret.Append(real.ToString(format, formatProvider));
            if (imag < 0)
            {
                ret.Append(" ");
            }
            else
            {
                ret.Append(" + ");
            }
            ret.Append(imag.ToString(format, formatProvider)).Append("i");

            return ret.ToString();
        }

        /// <summary>Creates a <b>Complex</b> based on a string. The string can be in the
        ///following formats: <b>n</b>, <b>ni</b>, <b>n +/- ni</b>, <b>n,n</b>, <b>n,ni</b>,
        ///<b>(n,n)</b>, or <b>(n,ni)</b>, where n is a real number.</summary>
        /// <param name="value">the string to parse.</param>
        /// <returns>a <b>Complex</b> containing the value of the string.</returns>
        public static Complex Parse(String value)
        {
            return Parse(value, null);
        }

        /// <summary>Creates a <b>Complex</b> based on a string. The string can be in the
        ///following formats: <b>n</b>, <b>ni</b>, <b>n +/- ni</b>, <b>n,n</b>, <b>n,ni</b>,
        ///<b>(n,n)</b>, or <b>(n,ni)</b>, where n is a real number.</summary>
        /// <param name="value">the string to parse.</param>
        ///<param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        /// <returns>a <b>Complex</b> containing the value of the string.</returns>
        public static Complex Parse(String value, IFormatProvider formatProvider)
        {
            if (value == null)
            {
                throw new ArgumentNullException(value, Strings.NullParameterException);
            }
            value = value.Trim();
            if (value.Length == 0)
            {
                throw new FormatException();
            }

            //check if one character strings are valid
            if (value.Length == 1)
            {
                if (String.Compare(value, "i") == 0)
                {
                    return new Complex(0, 1);
                }
                else
                {
                    return new Complex(Double.Parse(value, formatProvider));
                }
            }

            //strip out parens
            if (value.StartsWith("("))
            {
                if (!value.EndsWith(")"))
                {
                    throw new FormatException();
                }
                else
                {
                    value = value.Substring(1, value.Length - 2);
                }
            }

            string real = value;
            string imag = "0";

            //comma separated
            int index = value.IndexOf(',');
            if (index > -1)
            {
                real = value.Substring(0, index);
                imag = value.Substring(index + 1, value.Length - index - 1);
            }
            else
            {
                index = value.IndexOf('+', 1);
                if (index > -1)
                {
                    real = value.Substring(0, index);
                    imag = value.Substring(index + 1, value.Length - index - 1);
                }
                else
                {
                    index = value.IndexOf('-', 1);
                    if (index > -1)
                    {
                        real = value.Substring(0, index);
                        imag = value.Substring(index, value.Length - index);
                    }
                }
            }

            //see if we have numbers in the format xxxi
            if (real.EndsWith("i"))
            {
                if (!imag.Equals("0"))
                {
                    throw new FormatException();
                }
                else
                {
                    imag = real.Substring(0, real.Length - 1);
                    real = "0";
                }
            }
            if (imag.EndsWith("i"))
            {
                imag = imag.Substring(0, imag.Length - 1);
            }
            //handle cases of - n, + n
            if (real.StartsWith("-"))
            {
                real = "-" + real.Substring(1, real.Length - 1).Trim();
            }
            if (imag.StartsWith("-"))
            {
                imag = "-" + imag.Substring(1, imag.Length - 1).Trim();
            }

            Complex ret;
            try
            {
                ret = new Complex(Double.Parse(real.Trim(), formatProvider), Double.Parse(imag.Trim(), formatProvider));
            }
            catch (Exception)
            {
                throw new FormatException();
            }
            return ret;
        }

        #endregion Public Members
    }
}
